﻿using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;

namespace KineticScroll
{
	/// <summary>
	/// The page that will be shown in Tools -> Options for the user to edit Kinetic Scroll settings.
	/// </summary>
	[Guid(PackageConstants.OptionsPageGuid)]
	[ComVisible(true)]
	[TypeConverter(typeof(PropertySorter))]
	[PropertyPageTypeConverter(typeof(PropertySorter))]
	public class OptionsPage : DialogPage
	{
		#region Constants

		private const string KineticScrollCategory = "Kinetic Scroll";

		#endregion Constants

		#region Variables

		private string _installedVersion;

		private bool _middleClickEnabled = Settings.MiddleClickEnabledDefaultValue;
		private bool _mouseWheelEnabled = Settings.MouseWheelEnabledDefaultValue;

		private double _refreshInterval = Settings.RefreshIntervalDefaultValue;
		private double _deceleration = Settings.DecelerationDefaultValue;
		private double _maxVelocity = Settings.MaxVelocityDefaultValue;

		private double _mouseWheelAccelerationFactor = Settings.MouseWheelAccelerationFactorDefaultValue;

		#endregion Variables

		#region Properties

		/// <summary>
		/// Gets or sets the installed version.
		/// </summary>
		/// <remarks>
		/// This doesn't get shown to the user but is used to save this setting to the Visual Studio environment properties.
		/// </remarks>
		/// <value>The installed version.</value>
		[Browsable(false)]
		public string InstalledVersion
		{
			get
			{
				return _installedVersion;
			}
			set
			{
				_installedVersion = value;
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether [middle click enabled].
		/// </summary>
		/// <value><c>true</c> if [middle click enabled]; otherwise, <c>false</c>.</value>
		[Category(OptionsPage.KineticScrollCategory)]
		[PropertyOrder(1)]
		[DefaultValue(Settings.MiddleClickEnabledDefaultValue)]
		[Description("Whether kinetic scrolling is enabled when clicking the middle mouse button and dragging or flicking")]
		public bool MiddleClickEnabled
		{
			get
			{
				return _middleClickEnabled;
			}
			set
			{
				_middleClickEnabled = value;
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether [mouse wheel enabled].
		/// </summary>
		/// <value><c>true</c> if [mouse wheel enabled]; otherwise, <c>false</c>.</value>
		[Category(OptionsPage.KineticScrollCategory)]
		[PropertyOrder(2)]
		[DefaultValue(Settings.MouseWheelEnabledDefaultValue)]
		[Description("Whether kinetic scrolling is enabled when scrolling with the mouse wheel")]
		public bool MouseWheelEnabled
		{
			get
			{
				return _mouseWheelEnabled;
			}
			set
			{
				_mouseWheelEnabled = value;
			}
		}

		/// <summary>
		/// Gets or sets the refresh interval.
		/// </summary>
		/// <value>The refresh interval.</value>
		[Category(OptionsPage.KineticScrollCategory)]
		[PropertyOrder(3)]
		[DefaultValue(Settings.RefreshIntervalDefaultValue)]
		[Description("The number of milliseconds between refreshing the scroll position")]
		public double RefreshInterval
		{
			get
			{
				return _refreshInterval;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("RefreshInterval must be greater than 0");
				}

				_refreshInterval = value;
			}
		}

		/// <summary>
		/// Gets or sets the deceleration.
		/// </summary>
		/// <value>The deceleration.</value>
		[Category(OptionsPage.KineticScrollCategory)]
		[PropertyOrder(4)]
		[DefaultValue(Settings.DecelerationDefaultValue)]
		[Description("The deceleration (in pixels per millisecond per millisecond) to apply to scrolling each interval")]
		public double Deceleration
		{
			get
			{
				return _deceleration;
			}
			set
			{
				if (value < 0)
				{
					throw new ArgumentException("Deceleration must not be less than 0");
				}

				_deceleration = value;
			}
		}

		/// <summary>
		/// Gets or sets the max velocity.
		/// </summary>
		/// <value>The max velocity.</value>
		[Category(OptionsPage.KineticScrollCategory)]
		[PropertyOrder(5)]
		[DefaultValue(Settings.MaxVelocityDefaultValue)]
		[Description("The maximum scrolling velocity (in pixels per millisecond)")]
		public double MaxVelocity
		{
			get
			{
				return _maxVelocity;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentException("MaxVelocity must be greater than 0");
				}

				_maxVelocity = value;
			}
		}

		/// <summary>
		/// Gets or sets the mouse wheel acceleration factor.
		/// </summary>
		/// <value>The mouse wheel acceleration factor.</value>
		[Category(OptionsPage.KineticScrollCategory)]
		[PropertyOrder(6)]
		[DefaultValue(Settings.MouseWheelAccelerationFactorDefaultValue)]
		[Description("The acceleration factor to apply to velocity each time a mouse wheel event is raised")]
		public double MouseWheelAccelerationFactor
		{
			get
			{
				return _mouseWheelAccelerationFactor;
			}
			set
			{
				if (value < 1)
				{
					throw new ArgumentException("MouseWheelAccelerationFactor must be greater or equal than 1");
				}

				_mouseWheelAccelerationFactor = value;
			}
		}

		#endregion Properties

		#region Event Handlers

		/// <summary>
		/// Handles Apply messages from the Visual Studio environment.
		/// </summary>
		/// <devdoc>
		/// This method is called when VS wants to save the user's changes then the dialog is dismissed.
		/// </devdoc>
		protected override void OnApply(PageApplyEventArgs e)
		{
			base.OnApply(e);

			if (e.ApplyBehavior == ApplyKind.Apply)
			{
				Settings.Instance.MiddleClickEnabled = _middleClickEnabled;
				Settings.Instance.MouseWheelEnabled = _mouseWheelEnabled;
				Settings.Instance.RefreshInterval = _refreshInterval;
				Settings.Instance.Deceleration = _deceleration;
				Settings.Instance.MaxVelocity = _maxVelocity;
				Settings.Instance.MouseWheelAccelerationFactor = _mouseWheelAccelerationFactor;
			}
		}

		#endregion Event Handlers
	}
}